/**
 * 斗地主牌型检测
 */

import { CARD } from "../poker/card";
import { PokerSet } from "../poker/set";
import { LANDLORD_TYPE, SetInfo } from "./setInfo";

export class landLordChecker {

  //private setType: number;

  constructor() { }

  public GetSetInfo(set: PokerSet): SetInfo | string {
    switch (set.CountCards()) {
      case 0: return "玩家出牌为空";
      // 单张
      case 1: return new SetInfo(LANDLORD_TYPE.SINGLE, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(0).GetValue());
      // 对子或者王炸
      case 2:
        if (this.isPair(set)) {
          return new SetInfo(LANDLORD_TYPE.PAIR, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(0).GetValue());
        } else {
          return new SetInfo(LANDLORD_TYPE.JOKER_BOMB, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(1).GetValue());
        }
      // 三张
      case 3: return this.checkThreePlus(set);
      // 炸弹或三带一
      case 4:
        if (this.isCommonBomb(set)) {
          return new SetInfo(LANDLORD_TYPE.COMMON_BOMB, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(0).GetValue());
        }
        return this.checkThreePlus(set);
      // 三带二或者一条龙
      case 5:
        if (this.isDragon(set)) {
          return new SetInfo(LANDLORD_TYPE.DRAGON, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(set.CountCards() - 1).GetValue());
        }
        return this.checkThreePlus(set);
      default:
        if (this.isDragon(set)) {
          return new SetInfo(LANDLORD_TYPE.DRAGON, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(set.CountCards() - 1).GetValue());
        }
        if (this.isMultiPair(set)) {
          return new SetInfo(LANDLORD_TYPE.MULIT_PAIRS, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(set.CountCards() - 1).GetValue());
        }
        if (typeof this.checkFourPlus(set) != "string") {
          return this.checkFourPlus(set);
        } else if (typeof this.checkMultiFourPlus(set) != "string") {
          return this.checkMultiFourPlus(set);
        } else {
          return this.checkMultiThreePlus(set);
        }
    }
  }

  private isPair(set: PokerSet): boolean {
    if (set.CountCards() != 2) {
      return false;
    }

    if (set.GetPokerByIndex(0).GetValue() == set.GetPokerByIndex(1).GetValue()) {
      return true;
    }
    return false;
  }

  private isMultiPair(set: PokerSet): boolean {
    const pokersNum = set.CountCards();
    if (pokersNum % 2 != 0 || pokersNum < 6) {
      return false;
    }

    set.SortAsc();

    // 2和王不能作为连对出牌
    if (set.GetPokerByIndex(pokersNum - 1).GetValue() >= CARD.VALUE.TWO) {
      return false;
    }

    let tempValue = -1;
    for (let i = 0; i < pokersNum; i++) {
      const card = set.GetPokerByIndex(i);
      if (i == 0) {
        tempValue = card.GetValue();
      } else {
        if (i % 2 == 1) {
          if (card.GetValue() != tempValue) {
            return false;
          }
        } else {
          if (card.GetValue() == tempValue + 1) {
            tempValue = card.GetValue();
          } else {
            return false;
          }
        }
      }
    }
    return true;
  }

  private isJokerBomb(set: PokerSet): boolean {
    if (set.CountCards() != 2) {
      return false;
    }

    set.SortAsc();

    if (set.GetPokerByIndex(0).GetValue() == CARD.VALUE.BLACK_JOKER && set.GetPokerByIndex(1).GetValue() == CARD.VALUE.RED_JOKER) {
      return true;
    }
    return false;
  }

  private isCommonBomb(set: PokerSet): boolean {
    if (set.CountCards() != 4) {
      return false;
    }

    set.SortAsc();

    if (set.GetPokerByIndex(0).GetValue() == set.GetPokerByIndex(1).GetValue() &&
      set.GetPokerByIndex(0).GetValue() == set.GetPokerByIndex(2).GetValue() &&
      set.GetPokerByIndex(0).GetValue() == set.GetPokerByIndex(3).GetValue()) {
      return true;
    }
    return false;
  }

  private isDragon(set: PokerSet): boolean {
    const pokersNum = set.CountCards();
    if (pokersNum < 5) {
      return false;
    }

    set.SortAsc();

    // 2和王不能参与顺子出牌
    if (set.GetPokerByIndex(pokersNum - 1).GetValue() >= CARD.VALUE.TWO) {
      return false;
    }

    let tempValue = -1;
    for (let i = 0; i < pokersNum; i++) {
      const card = set.GetPokerByIndex(i);
      if (i == 0) {
        tempValue = card.GetValue();
      } else {
        if (card.GetValue() == tempValue + 1) {
          tempValue = card.GetValue();
        } else {
          return false;
        }
      }
    }
    return true;
  }

  private checkThreePlus(set: PokerSet): SetInfo | string {
    const pokersNum = set.CountCards();
    if (pokersNum < 3 || pokersNum > 5) {
      return "不是三带牌";
    }

    set.SortAsc();
    const cardMap = set.AnalyzeEachCardValueNum();
    if (pokersNum == 3) {
      if (cardMap.size == 1) {
        return new SetInfo(LANDLORD_TYPE.THREE, set.GetPokerByIndex(0).GetValue(), set.GetPokerByIndex(0).GetValue());
      } else {
        return "不是三带牌";
      }
    } else {
      if (cardMap.size == 2) {
        for (const [key, value] of cardMap.entries()) {
          if (value == 3) {
            if (pokersNum == 4) {
              return new SetInfo(LANDLORD_TYPE.THREE_PLUS_ONE, key, key);
            } else {
              return new SetInfo(LANDLORD_TYPE.THREE_PLUS_TWO, key, key);
            }
          }
        }
        return "不是三带牌";
      } else {
        return "不是三带牌";
      }
    }
  }

  private checkFourPlus(set: PokerSet): SetInfo | string {
    const pokersNum = set.CountCards();
    if (pokersNum != 6 && pokersNum != 8) {
      return "不是四带牌";
    }

    set.SortAsc();
    const cardMap = set.AnalyzeEachCardValueNum();
    if (cardMap.size == 2) {
      let key1 = -1;
      let key2 = -1;
      let value1 = -1;
      let value2 = -1;
      let i = 1;
      for (const [key, value] of cardMap.entries()) {
        if (i == 1) {
          key1 = key;
          value1 = value;
        } else {
          key2 = key;
          value2 = value;
        }
        i++;
      }

      // 支持444455这种四带二
      if (pokersNum == 6) {
        if (value1 == 4) {
          return new SetInfo(LANDLORD_TYPE.FOUR_PLUS_TWO, key1, key1);
        } else if (value2 == 4) {
          return new SetInfo(LANDLORD_TYPE.FOUR_PLUS_TWO, key2, key2);
        } else {
          return "不是四带牌";
        }
      }
      // 不支持44445555这种连续四带四的牌,因为没法判断是四带四还是多连四
      // 支持44446666这种牌型，以大的作为主牌，即四个6带两对4
      else {
        if (value1 == 4 && key1 != key2 + 1 && key2 != key1 + 1) {
          let masterKey = -1;
          if (key1 > key2) {
            masterKey = key1;
          } else {
            masterKey = key2;
          }
          return new SetInfo(LANDLORD_TYPE.FOUR_PLUS_FOUR, masterKey, masterKey);
        } else {
          return "不是四带牌";
        }
      }
    } else if (cardMap.size == 3) {
      let masterKey = -1;
      for (const [key, value] of cardMap.entries()) {
        if (value == 4) {
          masterKey = key;
        }
        if (pokersNum == 6) {
          if (value == 2 || value == 3 || value == 5 || value == 6) {
            return "不是四带牌";
          }
        } else {
          if (value == 1 || value == 3 || value == 5 || value == 6) {
            return "不是四带牌";
          }
        }
      }
      if (masterKey == -1) {
        return "不是四带牌";
      } else {
        if (pokersNum == 6) { // 四带二444456这种
          return new SetInfo(LANDLORD_TYPE.FOUR_PLUS_TWO, masterKey, masterKey);
        } else { // 四带四44445566这种
          return new SetInfo(LANDLORD_TYPE.FOUR_PLUS_FOUR, masterKey, masterKey);
        }
      }
    } else {
      return "不是四带牌";
    }
  }

  /**
   * 是否多个三带一, 或三代二, 或不带
   * @param set
   * @returns
   */
  private checkMultiThreePlus(set: PokerSet): SetInfo | string {
    const pokersNum = set.CountCards();
    if (pokersNum < 6) {
      return "不是三顺";
    }

    set.SortAsc();
    const cardMap = set.AnalyzeEachCardValueNum();

    let mainCardNum = 0; // 主牌的数量
    let mainCardValues = new Array<number>(); // 存放主牌的值
    let attachCardNum = 0; // 副牌的数量
    let attachCardNumMap = new Map<number, number>(); // 副牌的value和num的map

    for (const [key, value] of cardMap.entries()) {
      if (value == 3) {
        mainCardValues.push(key);
      } else {
        attachCardNumMap.set(key, value);
        attachCardNum += value;
      }
    }

    mainCardValues.sort((a, b) => a > b ? 1 : -1); // 从小到大排序
    let realMainCardValues = new Array<number>(); // 只包含连续的主牌的数量，不连续的同数量的当做附牌对待
    // 主牌连续，且只有一个连续的，其他的间断连续作为附牌处理
    const mainCardLength = mainCardValues.length;
    for (let i = 0; i < mainCardLength; i++) {
      const value = mainCardValues[i];
      if (i < mainCardLength - 1 && mainCardValues[i] + 1 == mainCardValues[i + 1]) {
        if (mainCardLength > 0 && value == realMainCardValues[mainCardLength - 1] + 1) {
          realMainCardValues.push(value);
        } else if (mainCardLength == 0) {
          realMainCardValues.push(value);
        } else {
          attachCardNumMap.set(value, 3);
          attachCardNum += 3
        }
      } else if (i > 0 && mainCardValues[i] == mainCardValues[i - 1] + 1) {
        if (mainCardLength > 0 && value == realMainCardValues[mainCardLength - 1] + 1) {
          realMainCardValues.push(value);
        } else if (mainCardLength == 0) {
          realMainCardValues.push(value);
        } else {
          attachCardNumMap.set(value, 3);
          attachCardNum += 3
        }
      } else { // 该值的牌作为附牌对待
        attachCardNumMap.set(value, 3);
        attachCardNum += 3
      }
    }

    mainCardNum = realMainCardValues.length;
    if (mainCardNum < 2) { // 未构成连续牌型
      return "不是三顺";
    }

    // 2和王不能参与连顺
    if (mainCardNum > 1 && realMainCardValues[mainCardNum - 1] > CARD.VALUE.ACE) {
      return "不是三顺";
    }

    // 没有附牌
    if (attachCardNum == 0) {
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else if (mainCardNum == attachCardNum) { // 三带一
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE_PLUS_ONE, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else if (mainCardNum * 2 == attachCardNum) { // 三带二
      for (const value of attachCardNumMap.values()) {
        if (value != 2 && value != 4) {
          return "不是三顺";
        }
      }
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE_PLUS_TWO, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else {
      return "不是三顺";
    }
  }

  /**
   * 是否多个四带一或四代二，或不带
   * @param set
   * @returns
   */
  private checkMultiFourPlus(set: PokerSet): SetInfo | string {
    const pokersNum = set.CountCards();
    if (pokersNum < 8 || pokersNum % 2 != 0) {
      return "不是四顺";
    }

    set.SortAsc();
    const cardMap = set.AnalyzeEachCardValueNum();

    let mainCardNum = 0; // 主牌的数量
    let mainCardValues = new Array<number>(); // 存放主牌的值
    let attachCardNum = 0; // 副牌的数量
    let attachCardNumMap = new Map<number, number>(); // 副牌的value和num的map

    for (const [key, value] of cardMap.entries()) {
      if (value == 4) {
        mainCardValues.push(key);
      } else {
        attachCardNumMap.set(key, value);
        attachCardNum += value;
      }
    }

    mainCardValues.sort((a, b) => a > b ? 1 : -1); // 从小到大排序
    let realMainCardValues = new Array<number>(); // 只包含连续的主牌的数量，不连续的同数量的当做附牌对待
    // 主牌连续，且只有一个连续的，其他的间断连续作为附牌处理
    const mainCardLength = mainCardValues.length;
    for (let i = 0; i < mainCardLength; i++) {
      const value = mainCardValues[i];
      if (i < mainCardLength - 1 && mainCardValues[i] + 1 == mainCardValues[i + 1]) {
        if (mainCardLength > 0 && value == realMainCardValues[mainCardLength - 1] + 1) {
          realMainCardValues.push(value);
        } else if (mainCardLength == 0) {
          realMainCardValues.push(value);
        } else {
          attachCardNumMap.set(value, 4);
          attachCardNum += 4
        }
      } else if (i > 0 && mainCardValues[i] == mainCardValues[i - 1] + 1) {
        if (mainCardLength > 0 && value == realMainCardValues[mainCardLength - 1] + 1) {
          realMainCardValues.push(value);
        } else if (mainCardLength == 0) {
          realMainCardValues.push(value);
        } else {
          attachCardNumMap.set(value, 4);
          attachCardNum += 4
        }
      } else { // 该值的牌作为附牌对待
        attachCardNumMap.set(value, 4);
        attachCardNum += 4
      }
    }

    mainCardNum = realMainCardValues.length;
    if (mainCardNum < 2) { // 未构成连续牌型
      return "不是四顺";
    }

    // 2和王不能参与连顺
    if (mainCardNum > 1 && realMainCardValues[mainCardNum - 1] > CARD.VALUE.ACE) {
      return "不是四顺";
    }

    // 没有附牌
    if (attachCardNum == 0) {
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else if (mainCardNum == attachCardNum) { // 四带二
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE_PLUS_ONE, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else if (mainCardNum * 2 == attachCardNum) { // 四带四
      for (const value of attachCardNumMap.values()) {
        if (value != 2 && value != 4) {
          return "不是四顺";
        }
      }
      return new SetInfo(LANDLORD_TYPE.MULITY_THREE_PLUS_TWO, realMainCardValues[0], realMainCardValues[mainCardNum - 1]);
    } else {
      return "不是四顺";
    }
  }
}
